fix(ios): keep active chat, sidebar + Settings sheet across settings changes#4
Merged
Merged
Conversation
Any settings change (theme, composer shell, transcript font, …) bumps TWThemeStore.revision, and RootView keys `.id(themes.revision)` — so the whole tree is torn down/rebuilt to re-read TWTheme's computed-static tokens. That reset the open chat, collapsed expanded workspaces, and re-expanded collapsed section headers every time. Hoist the transient UI state onto RemoteSessionModel (survives the teardown): selectedTaskId + expandedWorkspaces / collapsedSections / collapsedParents. HomeView reads the sidebar sets via computed properties (call sites unchanged). iPhone navigation becomes selection-driven (navigationDestination(item:) + a Button row instead of NavigationLink) so the open chat re-pushes from the surviving selectedTaskId after the rebuild; iPad already restores via its selection-bound detail column. Bonus: navigationTarget deep-links now also push correctly on iPhone. Verified: swift build + 69 Kit tests + iOS app build (iPhone 17 sim). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the prior commit. The Settings sheet was presented from HomeView, inside the `.id(themes.revision)` subtree — so changing a setting tore it down and closed it (you had to reopen for each change). Present it from RootView instead (above the teardown) via model.settingsPresented; it survives the rebuild and re-themes live through AppSettingsSheet's own @ObservedObject themes — no flash, no internal-state loss. Verified: swift build + iOS app build (iPhone 17 sim). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
boggspa
added a commit
that referenced
this pull request
Jun 18, 2026
Build 21 ships the merged iOS work: turn-based guest participation (PR #3, Mac-side bridge), active-chat/sidebar/Settings-sheet state preservation across settings changes (PR #4), and surfaced new global/ensemble chat create failures with retry instead of an infinite spinner (PR #5). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On iOS, changing any setting (theme, composer shell, transcript font, …) reset the workspaces sidebar, unloaded the open chat, re-expanded collapsed section headers, and closed the Settings sheet itself — so you lost your place every time you touched settings.
Root cause
TWThemeStorebumps a@Published revisionon every preference change, andRootViewkeys.id(themes.revision)(AppShell.swift). BecauseTWThemetokens are computed statics (not reactive), tearing down + rebuilding the whole tree is how they re-read on a theme change. That teardown wiped the transient@Statebelow it: the open chat, the sidebar expand/collapse Sets, and the Settings sheet's own presentation flag.Fix (2 commits)
1 — active chat + sidebar (
f98f6d24): hoist the transient UI state ontoRemoteSessionModel(created aboveRootView, survives the teardown):selectedTaskId+expandedWorkspaces/collapsedSections/collapsedParents.HomeViewreads the Sets via computed properties (call sites unchanged). iPhone navigation becomes selection-driven (navigationDestination(item:)+ aButtonrow) so the open chat re-pushes from the surviving id; iPad already restored via its selection-bound detail column. Bonus:navigationTargetdeep-links now push on iPhone too.2 — Settings sheet (
714bda73): the sheet was presented fromHomeView(inside the teardown), so it closed on each change. Present it fromRootViewinstead (attached after.id(themes.revision), on the stable outer view) viamodel.settingsPresented. It stays open across changes and re-themes live throughAppSettingsSheet's own@ObservedObject themes— no flash, no internal-state loss.Result: change theme / composer / font and you stay on your chat, with the sidebar exactly as you left it and the Settings sheet still open.
Verification
swift build(package) + 69 Kit tests + iOS app build (iPhone 17 sim) — all green on both commits.Note: this preserves state across the existing teardown (lowest-risk). The deeper follow-up — making
TWThemereactive to drop the.id()teardown entirely (removing the brief re-render flash) — is a large refactor across every token call site. (ConnectedShell.shellModefor files/diff still resets, but Settings isn't reachable from those modes, so it's not hit in practice.)🤖 Generated with Claude Code